﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Text.RegularExpressions;
using System.Web;

namespace Utilities.StringLib
{
    public class ProcessString
    {
        /// <summary>
        /// Convert a string, which has been segmented into words, into an array of words. 
        /// </summary>
        /// <param name="splitText">The segmented string</param>
        /// <returns>An array of words</returns>
        public static string[] ProcessSplitText(string splitText)
        {
            char[] seperatedChars = new char[] { ' ', '.', ';', '!', ':', ',', '(', ')', '[', ']', '{', '}', '\\', '/', '\'', '<', '>' };
            splitText = ProcessString.StripDiacritics(splitText);
            splitText = splitText.ToUpper();
            string[] result = splitText.Split(seperatedChars, StringSplitOptions.RemoveEmptyEntries);

            for (int i = 0; i < result.Length; i++)
            {
                result[i] = result[i].Replace('_', ' ');
            }

            return result;
        }

        /// <summary>
        /// Remove duplicated strings in the @processedStr
        /// </summary>
        /// <param name="processedStr">The string which will be processed</param>
        /// <param name="duplicatedStrs">Duplicated strings</param>
        /// <returns>The result string without duplicates strings</returns>
        public static string RemoveDuplicatedStrings(string processedStr, params string[] duplicatedStrs)
        {
            for (int i = 0; i < duplicatedStrs.Length; i++)
            {
                int dupLength = duplicatedStrs[i].Length;
                for (int j = 0; j <= processedStr.Length - dupLength; j++)
                {
                    if (processedStr.Substring(j, dupLength) == duplicatedStrs[i])
                    {
                        if (j == 0) //-abc
                        {
                            processedStr = processedStr.Substring(dupLength);
                            j--;
                        }
                        else if (j == processedStr.Length - dupLength) //abc-
                        {
                            processedStr = processedStr.Substring(0, j);
                        }
                        else if ((j + 2 * dupLength <= processedStr.Length) && (processedStr.Substring(j + dupLength, dupLength) == duplicatedStrs[i]))
                        {
                            processedStr = processedStr.Substring(0, j) + processedStr.Substring(j + dupLength);
                            j--;
                        }
                    }
                }
            }

            return processedStr;
        }

        /// <summary>
        /// Render the string
        /// </summary>
        /// <param name="renderStr">The string which will be rendered</param>
        /// <returns>The string after being rendered</returns>
        public static string RenderString(string renderedStr)
        {
            renderedStr = HttpUtility.HtmlDecode(renderedStr);
            //int startPos = 0;
            //int endPos = 0;

            //do
            //{
            //    startPos = renderedStr.IndexOf("&#", startPos); // The begining sign of an un-readable char
            //    if (startPos != -1)
            //    {
            //        endPos = renderedStr.IndexOf(";", startPos); // The ending sign of an un-readable char
            //        if (endPos != -1)
            //        {
            //            string beginStr = renderedStr.Substring(0, startPos);
            //            string endStr = renderedStr.Substring(endPos + 1);
            //            string midStr = renderedStr.Substring(startPos + 2, endPos - startPos - 2);

            //            try
            //            {
            //                midStr = ((char)(int.Parse(midStr))).ToString(); // Render the char
            //            }
            //            catch (Exception)
            //            {
            //                midStr = "?";
            //            }

            //            renderedStr = beginStr + midStr + endStr;
            //        }
            //        else
            //        {
            //            startPos = -1;
            //        }
            //    }
            //} while (startPos != -1);

            return renderedStr;
        }

        /// <summary>
        /// Replace unwanted chars in the string with wanted char
        /// </summary>
        /// <param name="processedStr">The string which will be processed</param>
        /// <param name="wantedChar">The wanted char which will replace unwanted chars</param>
        /// <param name="unwantedChars">The array of unwated chars which will be replaced</param>
        /// <returns>The string after being processed</returns>
        public static string ReplaceUnwantedChars(string processedStr, string wantedChar, params char[] unwantedChars)
        {
            for (int i = 0; i < unwantedChars.Length; i++)
            {
                // Replace the unwanted string (which has only one char) with the emty string
                processedStr = processedStr.Replace(unwantedChars[i].ToString(), wantedChar.ToString());
            }

            return processedStr;
        }

        /// <summary>
        /// Remove unwanted chars in the string
        /// </summary>
        /// <param name="unwantedChars">Array of unwanted chars</param>
        /// <param name="processedStr">The string which is processed</param>
        /// <returns>The result string</returns>
        public static string RemoveUnwantedChars(string processedStr, params char[] unwantedChars)
        {
            processedStr = ReplaceUnwantedChars(processedStr, string.Empty, unwantedChars);

            return processedStr;
        }

        /// <summary>
        /// Remove duplicated chars in the string
        /// </summary>
        /// <param name="duplicatedChars">Array of duplicated chars</param>
        /// <param name="processedStr">The string which is processed</param>
        /// <returns>The result string</returns>
        public static string RemoveDuplicatedChars(string processedStr, params char[] duplicatedChars)
        {
            for (int i = 0; i < duplicatedChars.Length; i++)
            {
                for (int j = 0; j < processedStr.Length; j++)
                {
                    if (processedStr[j] == duplicatedChars[i])
                    {
                        if (j == 0) //-abc
                        {
                            processedStr = processedStr.Substring(j + 1);
                            j--;
                        }
                        else if (j == processedStr.Length - 1) //abc-
                        {
                            processedStr = processedStr.Substring(0, processedStr.Length - 1);
                        }
                        else if (processedStr[j + 1] == duplicatedChars[i])
                        {
                            //abc--
                            if (j + 1 == processedStr.Length - 1)
                            {
                                processedStr = processedStr.Substring(0, j);
                            }
                            else //abc--abc
                            {
                                processedStr = processedStr.Substring(0, j + 1) + processedStr.Substring(j + 2);
                                j--;
                            }
                        }
                    }
                }
            }

            return processedStr;
        }

        /// <summary>
        /// Remove the empty rows in the string
        /// </summary>
        /// <param name="processedStr">The string which will be processed</param>
        /// <returns>The result string without empty rows</returns>
        public static string RemoveEmptyRow(string processedStr)
        {
            int first = 0;
            int second = 0;

            while (first < processedStr.Length && first != -1 && second != -1)
            {
                first = processedStr.IndexOf("\r\n", second);
                second = processedStr.IndexOf("\r\n", first + 2);

                if (first >= 0 && second >= 0)
                {
                    bool isEmpty = true;
                    for (int i = first; i < second; i++)
                    {
                        if (char.IsLetterOrDigit(processedStr[i]) == true)
                        {
                            isEmpty = false;
                        }
                    }

                    if (isEmpty == true)
                    {
                        processedStr = processedStr.Remove(first, second - first);
                        second = first;
                    }
                }
            }

            return processedStr;
        }

        /// <summary>
        /// Count the number of existence of all element in termLst in the plain text
        /// </summary>
        /// <param name="term">The termLst which will be counted</param>
        /// <param name="plaintext">The plain text which is used to count the term</param>
        /// <returns>The list result</returns>
        public static List<int> CountAllExistence(List<string> termLst, string plainText)
        {
            List<int> result = new List<int>();
            for (int i = 0; i < termLst.Count; i++)
            {
                result.Add(CountExistence(termLst[i], plainText));
            }

            return result;
        }

        /// <summary>
        /// Count the number of existence of the term in the plain text
        /// </summary>
        /// <param name="term">The term which will be counted</param>
        /// <param name="plaintext">The plain text which is used to count the term</param>
        /// <returns>The number of existence of the term</returns>
        public static int CountExistence(string term, string plainText)
        {
            int count = 0;
            int startPos = plainText.IndexOf(term, 0);
            while (startPos != -1)
            {
                count++;
                startPos = plainText.IndexOf(term, startPos + term.Length + 1);
            }

            return count;
        }

        /// <summary>
        /// Check if the string is "empty" or not
        /// </summary>
        /// <param name="processedTag">The string which will be processed</param>
        /// <returns>False if the string contains digits or letters, true otherwise</returns>
        public static bool IsSpecialEmptyStr(string processedTag)
        {
            processedTag = processedTag.Replace("&nbsp;", " ");
            for (int i = 0; i < processedTag.Length; i++)
            {
                if (char.IsLetterOrDigit(processedTag[i]) == true)
                {
                    return false;
                }
            }

            return true;
        }

        #region Split Title Terms
        
        /// <summary>
        /// Strip all diacritics
        /// </summary>
        /// <param name="processedSTr">The string which will be strip diacritics</param>
        /// <returns>The string without diacriticsf</returns>
        public static string StripDiacritics(string processedStr)
        {
            Regex regex = new Regex(@"\p{IsCombiningDiacriticalMarks}+");

            string strFormD = processedStr.Normalize(NormalizationForm.FormD);
            return regex.Replace(strFormD, String.Empty).Replace('\u0111', 'd').Replace('\u0110', 'D');
        }

        /// <summary>
        /// Split the @processedArrStr[titles] to terms according to @coreArrString[meta]
        /// </summary>
        /// <param name="coreArrStr"></param>
        /// <param name="processedArrStr"></param>
        /// <returns></returns>
        public static string[] SplitTermsAccordingToCoreStrings(string[] coreArrStr, params string[] processedArrStr)
        {
            if (processedArrStr != null)
            {
                string[] result = new string[0];

                for (int j = 0; j < processedArrStr.Length; j++)
                {
                    int i = 0;
                    string[] tempResult = new string[0];

                    if (coreArrStr != null)
                    {
                        for (i = 0; i < coreArrStr.Length; i++)
                        {
                            if (processedArrStr[j].Contains(coreArrStr[i]))
                            {
                                tempResult = SplitString(processedArrStr[j].Replace(coreArrStr[i], "@"), '@');

                                result = CombineArrayStrings(result
                                    , (AddStrToArrString(SplitTermsAccordingToCoreStrings(SubStringArray(coreArrStr, i + 1), tempResult), coreArrStr[i])));

                                break;
                            }
                        }
                    }

                    if (tempResult.Length == 0)
                    {
                        result = AddStrToArrString(result, processedArrStr[j]);
                    }
                }

                result = RemoveDuplicatedStrElement(result);
                return result;
            }
            else
            {
                return null;
            }
        }

        public static List<string> SplitTermsAccordingToCoreStrings1(string[] coreArrStr, string[] processedArrStr)
        {
            List<string> result = new List<string>();
            for (int i = 0; i < coreArrStr.Length; i++)
            {
                int j = 0;
                for (; j < processedArrStr.Length; j++)
                {
                    if (processedArrStr[j].Contains(coreArrStr[i]))
                    {
                        result.Add(coreArrStr[i]);
                        break;
                    }                    
                }

                if (j < processedArrStr.Length)
                {
                    for (; j < processedArrStr.Length; j++)
                    {
                        processedArrStr[j] = processedArrStr[j].Replace(coreArrStr[i], "@");
                    }
                }
            }

            foreach (string str in processedArrStr)
            {
                string[] temp = str.Split(new char[] { '@' }, StringSplitOptions.RemoveEmptyEntries);
                result.AddRange(temp);
            }

            return result;
        }

        public static List<string> SegmentSentences(List<string> sentences, string[] coreStrings)
        {
            for (int i = 0; i < sentences.Count; i++)
            {
                sentences[i] = SplitWordAccordingToCoreString(sentences[i], coreStrings);                                
            }

            return sentences;
        }

        private static string SplitWordAccordingToCoreString(string sentences, string[] coreStrings)
        {            
            for (int i = 0; i < coreStrings.Length; i++)
            {                
                if (sentences.Contains(coreStrings[i]))
                {
                    string temp = RemoveDuplicatedChars(coreStrings[i], ' ');
                    temp = temp.Replace(' ', '_');
                    sentences = sentences.Replace(coreStrings[i], temp);
                }
            }

            return sentences;
        }

        /// <summary>
        /// Remove duplicated elements and keep only one instance in an array of string
        /// </summary>
        /// <param name="processedArrStr">The string array which will be processed</param>
        /// <returns>The result string which has no duplicated elements</returns>
        public static string[] RemoveDuplicatedStrElement(string[] processedArrStr)
        {
            ArrayList result = new ArrayList();
            for (int i = 0; i < processedArrStr.Length; i++)
            {
                bool isDuplicated = false;
                for (int j = i + 1; j < processedArrStr.Length; j++)
                {
                    if (processedArrStr[i].CompareTo(processedArrStr[j]) == 0)
                    {
                        isDuplicated = true;
                    }
                }

                if (isDuplicated == false)
                {
                    result.Add(processedArrStr[i]);
                }
            }
            return (string[])(result.ToArray(typeof(string)));
        }

        /// <summary>
        /// Add a string to an array of strings to make a new string array
        /// </summary>
        /// <param name="arrString">The array of strings</param>
        /// <param name="addedStr">The added string</param>
        /// <returns>The result string array</returns>
        public static string[] AddStrToArrString(string[] arrString, string addedStr)
        {
            if (arrString != null)
            {
                if (addedStr != null)
                {
                    string[] result = new string[arrString.Length + 1];
                    result[0] = addedStr;

                    int i = 1;
                    foreach (string str in arrString)
                    {
                        result[i++] = str;
                    }

                    return result;
                }
                else
                {
                    return arrString;
                }
            }
            else
            {
                if (addedStr != null)
                {
                    return new string[] { addedStr };
                }
                else
                {
                    return null;
                }
            }
        }

        /// <summary>
        /// Combine arrays of strings to make a unique string array
        /// </summary>
        /// <param name="processedStrs">Arrays of strings</param>
        /// <returns>The unique string array</returns>
        public static string[] CombineArrayStrings(params string[][] processedStrs)
        {
            ArrayList result = new ArrayList();

            for (int i = 0; i < processedStrs.Length; i++)
            {
                if (processedStrs[i] != null)
                {
                    foreach (string str in processedStrs[i])
                    {
                        result.Add(str);
                    }
                }
            }

            if (result.Count != 0)
            {
                return (string[])(result.ToArray(typeof(string)));
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// Split the string according to @seperator array and delete empty strings
        /// </summary>
        /// <param name="processedStr">The string which will be processed</param>
        /// <param name="seperator">The seperator strings</param>
        /// <returns>The array of splitted strings</returns>
        public static string[] SplitString(string processedStr, params char[] seperator)
        {
            if (processedStr != null && seperator != null)
            {
                string[] tempArrStr = processedStr.Split(seperator);

                ArrayList tempArrList = new ArrayList();
                for (int i = 0; i < tempArrStr.Length; i++)
                {
                    if (tempArrStr[i] != string.Empty)
                    {
                        tempArrList.Add(tempArrStr[i].Trim());
                    }
                }

                return (string[])(tempArrList.ToArray(typeof(string)));
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// Get the sub-array of a string array from @startPos and take @count elements
        /// </summary>
        /// <param name="processedStr">The string array which is processed</param>
        /// <param name="startPos">The begin position of the sub-array to get</param>
        /// <param name="count">The number of element which will be taken</param>
        /// <returns>The sub-array if @startPos and @count are valid, null otherwise</returns>
        public static string[] SubStringArray(string[] processedStr, int startPos, int count)
        {
            if (processedStr != null)
            {
                ArrayList result = new ArrayList();

                if ((startPos >= 0) && (count > 0) && (startPos + count <= processedStr.Length))
                {
                    for (int i = startPos; i < startPos + count; i++)
                    {
                        result.Add(processedStr[i]);
                    }

                    return (string[])(result.ToArray(typeof(string)));
                }
                else
                {
                    return null;
                }
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// Get the sub-array of a string array from @startPos to the end of it
        /// </summary>
        /// <param name="processedStrArr">The string array which is processed</param>
        /// <param name="startPos">The begin position of the sub-array to get</param>
        /// <returns>The sub-array if @startPos is valid, null otherwise</returns>
        public static string[] SubStringArray(string[] processedStrArr, int startPos)
        {
            if (processedStrArr != null)
            {
                ArrayList result = new ArrayList();

                if ((startPos >= 0) && (startPos < processedStrArr.Length)) // if startPos is valid
                {
                    for (int i = startPos; i < processedStrArr.Length; i++)
                    {
                        result.Add(processedStrArr[i]);
                    }

                    return (string[])(result.ToArray(typeof(string)));
                }
                else
                {
                    return null;
                }
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// Normalize and Split the string
        /// </summary>
        /// <param name="processedStr">The string which is processed</param>
        /// <returns>The array of result strings</returns>
        public static string[] NormalizeAndSplitString(string processedStr)
        {
            if (processedStr != null)
            {
                processedStr = processedStr.ToLower(); // Lower all chars
                processedStr = StripDiacritics(processedStr); // Strip all diacritics

                string tempStr = "";

                // Remove irrelevant chars
                for (int i = 0; i < processedStr.Length; i++)
                {
                    // Ischaracter or ':' or '?' or ','
                    if ((processedStr[i] >= 'a' && processedStr[i] <= 'z') // the string contains all lower chars
                        || (processedStr[i] == ':' || processedStr[i] == '?' || processedStr[i] == ','))
                    {
                        tempStr += processedStr[i].ToString();
                    }
                }

                // Split the string to seperated parts
                string[] tempResult = processedStr.Split(new char[] { ',', ':', '?' });
                ArrayList result = new ArrayList();

                // Remove all empty strings
                for (int i = 0; i < tempResult.Length; i++)
                {
                    if (tempResult[i].Trim() != "")
                    {
                        result.Add(tempResult[i]);
                    }
                }

                return (string[])result.ToArray(typeof(string)); // Parse the arraylist to array of string.
            }
            else
            {
                return null;
            }
        }
        
        #endregion Split Title Terms
    }
}
